/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.vsu.csf.qasystem.morph.data;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern;
import ru.vsu.csf.qasystem.QAApplication;
import ru.vsu.csf.qasystem.morph.Adjective;
import ru.vsu.csf.qasystem.morph.Adverb;
import ru.vsu.csf.qasystem.morph.Word;
import ru.vsu.csf.qasystem.morph.Noun;
import ru.vsu.csf.qasystem.morph.Particle;
import ru.vsu.csf.qasystem.morph.Preposition;
import ru.vsu.csf.qasystem.morph.Proadjective;
import ru.vsu.csf.qasystem.morph.Proadverb;
import ru.vsu.csf.qasystem.morph.Pronoun;
import ru.vsu.csf.qasystem.morph.Union;
import ru.vsu.csf.qasystem.morph.Verb;
import ru.vsu.csf.qasystem.morph.enumtypes.AdjRank;
import ru.vsu.csf.qasystem.morph.enumtypes.Case;
import ru.vsu.csf.qasystem.morph.enumtypes.CompareDegree;
import ru.vsu.csf.qasystem.morph.enumtypes.Form;
import ru.vsu.csf.qasystem.morph.enumtypes.Gender;
import ru.vsu.csf.qasystem.morph.enumtypes.Inclination;
import ru.vsu.csf.qasystem.morph.enumtypes.Person;
import ru.vsu.csf.qasystem.morph.enumtypes.PronounType;
import ru.vsu.csf.qasystem.morph.enumtypes.Quantity;
import ru.vsu.csf.qasystem.morph.enumtypes.Tense;

/**
 *
 * @author Руслан
 */
public class MystemAdapter {
    
    private final String DICTIONARY_PATH  = "resources\\mystem_generated_dict.txt";
    
    private HashMap<String,Case> CASES;
    
    private HashMap<String,Gender> GENDERS;
    
    private HashMap<String,Quantity> QUANTS;
    
    private List<String> OTHERS;
    
    private HashMap<String,Form> FORMS;
    
    private HashMap<String,Tense> TENSES;
    
    private HashMap<String,Person> PERSONS;
    
    private HashMap<String,Inclination> INCLINATIONS;
    
    private List<String> PERSONAL_PRONOUNS;
    
    private List<String> INTERROGATIVE_PRONOUNS;
    
    private List<String> POSSESIVE_PRONOUNS;
    
    private List<String> INDETERMINATE_PRONOUNS;
    
    private List<String> NEGATIVE_PRONOUNS;
    
    private List<String> ATTRIBUTIVE_PRONOUNS;
    
    private List<String> INDICATORY_PRONOUNS;
    
    public MystemAdapter() {
        
        CASES = new HashMap<String, Case>();
        CASES.put("им", Case.Nominative);
        CASES.put("род", Case.Genitive);
        CASES.put("дат", Case.Dative);
        CASES.put("вин", Case.Accusative);
        CASES.put("твор", Case.Instrumental);
        CASES.put("пред", Case.Prepositional);
        
        GENDERS = new HashMap<String, Gender>();
        GENDERS.put("муж", Gender.Masculine);
        GENDERS.put("жен", Gender.Feminine);
        GENDERS.put("сред", Gender.Neuter);
        
        QUANTS = new HashMap<String, Quantity>();
        QUANTS.put("ед", Quantity.Singular);
        QUANTS.put("мн", Quantity.Plural);
        
        OTHERS = new ArrayList<String>();
        OTHERS.add("гео");
        OTHERS.add("имя");
        OTHERS.add("отч");
        OTHERS.add("фам");
        
        FORMS = new HashMap<String, Form>();
        FORMS.put("сов", Form.Perfect);
        FORMS.put("несов", Form.Imperfect);
        
        TENSES = new HashMap<String, Tense>();
        TENSES.put("наст", Tense.Present);
        TENSES.put("непрош", Tense.Present);
        TENSES.put("прош", Tense.Past);
        
        PERSONS = new HashMap<String, Person>();
        PERSONS.put("1-л", Person.First);
        PERSONS.put("2-л", Person.Second);
        PERSONS.put("3-л", Person.Third);
        
        INCLINATIONS = new HashMap<String, Inclination>();
        INCLINATIONS.put("изъяв", Inclination.Indicative);
        INCLINATIONS.put("пов", Inclination.Imperative);
        
        PERSONAL_PRONOUNS = new LinkedList<String>();
        PERSONAL_PRONOUNS.add("я");
        PERSONAL_PRONOUNS.add("мы");
        PERSONAL_PRONOUNS.add("ты");
        PERSONAL_PRONOUNS.add("он");
        PERSONAL_PRONOUNS.add("она");
        PERSONAL_PRONOUNS.add("оно");
        PERSONAL_PRONOUNS.add("вы");
        PERSONAL_PRONOUNS.add("они");
        
        INTERROGATIVE_PRONOUNS = new LinkedList<String>();
        INTERROGATIVE_PRONOUNS.add("кто");
        INTERROGATIVE_PRONOUNS.add("что");
        INTERROGATIVE_PRONOUNS.add("какой");
        INTERROGATIVE_PRONOUNS.add("чей");
        INTERROGATIVE_PRONOUNS.add("который");
        INTERROGATIVE_PRONOUNS.add("сколько");
        INTERROGATIVE_PRONOUNS.add("где");
        INTERROGATIVE_PRONOUNS.add("когда");
        INTERROGATIVE_PRONOUNS.add("куда");
        INTERROGATIVE_PRONOUNS.add("откуда");
        INTERROGATIVE_PRONOUNS.add("почему");
        INTERROGATIVE_PRONOUNS.add("зачем");
        INTERROGATIVE_PRONOUNS.add("как");
        
        POSSESIVE_PRONOUNS = new LinkedList<String>();
        POSSESIVE_PRONOUNS.add("мой");
        POSSESIVE_PRONOUNS.add("твой");
        POSSESIVE_PRONOUNS.add("наш");
        POSSESIVE_PRONOUNS.add("ваш");
        POSSESIVE_PRONOUNS.add("свой");
        
        INDETERMINATE_PRONOUNS = new LinkedList<String>();
        INDETERMINATE_PRONOUNS.add("некто");
        INDETERMINATE_PRONOUNS.add("нечто");
        INDETERMINATE_PRONOUNS.add("несколько");
        INDETERMINATE_PRONOUNS.add("кто-то");
        INDETERMINATE_PRONOUNS.add("что-то");
        INDETERMINATE_PRONOUNS.add("какой-то");
        INDETERMINATE_PRONOUNS.add("когда-то");
        INDETERMINATE_PRONOUNS.add("где-то");
        INDETERMINATE_PRONOUNS.add("кое-кто");
        
        NEGATIVE_PRONOUNS = new LinkedList<String>();
        NEGATIVE_PRONOUNS.add("никто");
        NEGATIVE_PRONOUNS.add("ничто");
        NEGATIVE_PRONOUNS.add("никакой");
        NEGATIVE_PRONOUNS.add("ничей");
        NEGATIVE_PRONOUNS.add("нигде");
        NEGATIVE_PRONOUNS.add("никогда");
        NEGATIVE_PRONOUNS.add("никуда");
        
        ATTRIBUTIVE_PRONOUNS = new LinkedList<String>();
        ATTRIBUTIVE_PRONOUNS.add("сам");
        ATTRIBUTIVE_PRONOUNS.add("весь");
        ATTRIBUTIVE_PRONOUNS.add("всякий");
        ATTRIBUTIVE_PRONOUNS.add("каждый");
        ATTRIBUTIVE_PRONOUNS.add("иной");
        ATTRIBUTIVE_PRONOUNS.add("другой");
        ATTRIBUTIVE_PRONOUNS.add("любой");
        ATTRIBUTIVE_PRONOUNS.add("всюду");
        ATTRIBUTIVE_PRONOUNS.add("везде");
        ATTRIBUTIVE_PRONOUNS.add("всегда");
        
        INDICATORY_PRONOUNS = new LinkedList<String>();
        INDICATORY_PRONOUNS.add("тот");
        INDICATORY_PRONOUNS.add("этот");
        INDICATORY_PRONOUNS.add("такой");
        INDICATORY_PRONOUNS.add("столько");
        INDICATORY_PRONOUNS.add("там");
        INDICATORY_PRONOUNS.add("тут");
        INDICATORY_PRONOUNS.add("туда");
        INDICATORY_PRONOUNS.add("тогда");
        INDICATORY_PRONOUNS.add("поэтому");
        INDICATORY_PRONOUNS.add("оттуда");
        INDICATORY_PRONOUNS.add("так");
        INDICATORY_PRONOUNS.add("отсюда");
        INDICATORY_PRONOUNS.add("сюда");
        INDICATORY_PRONOUNS.add("здесь");
    }

    public MorphDictionary loadDictionary() {
        MorphDictionary dict = new MorphDictionary();
        InputStream stream = QAApplication.class.getResourceAsStream(DICTIONARY_PATH);
        Integer count = 0;
        for (Word l : Union.getAllUnions()) {
            dict.addWordForm(l);
            count++;
            
        }
        for (Word l : Particle.getAllParticles()) {
            dict.addWordForm(l);
            count++;
        }
        for (Word l : Preposition.getAllPrepositions()) {
            dict.addWordForm(l);
            count++;
        }
        if (stream == null) System.out.println("could not find dictionary file");
        try {
            InputStreamReader streamReader = new InputStreamReader(stream, "Cp1251");
            BufferedReader in = new BufferedReader(streamReader);
            
            while (in.ready()) {
                String s = in.readLine();
                if (!s.isEmpty()) {
                    List<Word> list = readLine(s);
                    count += list.size();
                    if (!list.isEmpty()) {
                        dict.addWordForms(list.get(0).getText(), list);
                    }
                }
            }
            System.out.println("dictionary loaded. Words: " + dict.getWordCount() + " forms: " + count.toString());
        }
        catch (Exception ex) {
            System.out.println("reading dictionary file error: " );
            ex.printStackTrace(System.out);
        }
        return null;
    }
   

    private List<Word> readLine(String line) {
        List<Word> result = new LinkedList<Word>();
        Scanner sc = new Scanner(line);
        sc.useDelimiter("\\{|\\}|\\|");
        String word = sc.next();
        String mainForm = "";
        while (sc.hasNext()){
            String form = sc.next();
            System.out.println(form);
            Scanner sc2 = new Scanner(form);
            String mf = sc2.findInLine("[а-я]*");
            if (mf.isEmpty()) mf = mainForm;
            else mainForm = mf;
            String wordType = sc2.findInLine("[A-Z]+");
            if (wordType == null) continue;
            sc2.useDelimiter(",|=");
            List<String> parts = new LinkedList<String>();
            while(sc2.hasNext()) {
                parts.add(sc2.next());
            }
            Word lexem = null;
            if (wordType.equals("S")) {
                lexem = parseNoun(parts);
            }
            if (wordType.equals("A")) {
                lexem = parseAdjective(parts);
            }
            if (wordType.equals("V")) {
                lexem = parseVerb(parts);
            }
            if (wordType.equals("ADV")) {
                lexem = parseAdverb(parts);
            }
            if (wordType.equals("SPRO")) {
                lexem = parsePronoun(parts, mainForm);
            }
            if (wordType.equals("APRO")) {
                lexem = parseProAdjective(parts, mainForm);
            }
            if (wordType.equals("ADVPRO")) {
                lexem = parseProAdverb(parts, mainForm);
            }
            if (lexem != null) {
                lexem.setText(word);
                lexem.setMainForm(mainForm);
                result.add(lexem);
            }
        }
        return result;
    }
    
    private Word parseNoun(List<String> parts) {
        Noun noun = new Noun();
        if (parts.get(0).equals("сокр")) return null;
        if (OTHERS.contains(parts.get(0))) parts.remove(0);
        noun.setGender(GENDERS.get(parts.get(0)));
        noun.setWordcase(CASES.get(parts.get(2)));
        noun.setQuantity(QUANTS.get(parts.get(3)));
        return noun;
    }
    
    private Word parseAdjective(List<String> parts) {
        Adjective adj = new Adjective();
        if (parts.get(0).equals("срав")) {
            adj.setDegree(CompareDegree.Comparative);
            return adj;
        }
        if (parts.get(1).equals("притяж")) {
            adj.setRank(AdjRank.Possesive);
            adj.setIsFull(true);
            adj.setWordCase(CASES.get(parts.get(2)));
            adj.setQuantity(QUANTS.get(parts.get(3)));
            adj.setGender(GENDERS.get(parts.get(4)));
            return adj;
        }
        if (parts.get(1).equals("кр")) {
            adj.setIsFull(false);
            adj.setQuantity(QUANTS.get(parts.get(0)));
            adj.setGender(GENDERS.get(parts.get(2)));
            return adj;
        }
        adj.setIsFull(true);
        adj.setWordCase(CASES.get(parts.get(0)));
        adj.setQuantity(QUANTS.get(parts.get(1)));
        adj.setGender(GENDERS.get(parts.get(3)));
        return adj;
    }
    
    private Word parseVerb(List<String> parts) {
        Verb verb = new Verb();
        if (parts.get(1).equals("нп")) parts.remove(1);
        verb.setForm(FORMS.get(parts.get(0)));
        if (parts.get(1).equals("инф")) {
            verb.setIsInfinitive(true);
            return verb;
        }
        verb.setIsInfinitive(false);
        if (parts.get(2).equals("пов")) {
            verb.setInclination(Inclination.Imperative);
            verb.setQuantity(QUANTS.get(parts.get(1)));
            return verb;
        }
        verb.setTense(TENSES.get(parts.get(1)));
        verb.setQuantity(QUANTS.get(parts.get(2)));
        if (verb.getTense() == Tense.Past) {
            if (verb.getQuantity() == Quantity.Singular) {
                verb.setGender(GENDERS.get(parts.get(4)));
            }
            return verb;
        }
        else {
            verb.setPerson(PERSONS.get(parts.get(4)));
        }
        return null;
    }
    
    private Word parseAdverb(List<String> parts) {
        Adverb adv = new Adverb();
        if (!parts.isEmpty())
            if (parts.get(0).equals("срав")) adv.setDegree(CompareDegree.Comparative);
        return adv;
    }
    
    private Word parsePronoun(List<String> parts,String mainForm) {
        Pronoun pronoun = new Pronoun();
        if (parts.get(0).equals("сокр")) return null;
        if (PERSONAL_PRONOUNS.contains(mainForm)) {
            pronoun.setType(PronounType.Personal);
            pronoun.setQuantity(QUANTS.get(parts.get(0)));
            if (mainForm.equalsIgnoreCase("они")) {
                pronoun.setPerson(Person.Third);
                pronoun.setWordcase(CASES.get(parts.get(1)));
                return pronoun;
            }
            pronoun.setPerson(PERSONS.get(parts.get(1)));
            if (pronoun.getPerson() == Person.Third 
                    && pronoun.getQuantity() == Quantity.Singular) {
                pronoun.setGender(GENDERS.get(parts.get(2)));
                parts.remove(2);
            }
            pronoun.setWordcase(CASES.get(parts.get(2)));
            return pronoun;
        }
        if (INTERROGATIVE_PRONOUNS.contains(mainForm)) {
            pronoun.setType(PronounType.Interrogative);
            pronoun.setQuantity(QUANTS.get(parts.get(0)));
            pronoun.setGender(GENDERS.get(parts.get(1)));
            pronoun.setWordcase(CASES.get(parts.get(3)));
            return pronoun;
        }
        if (NEGATIVE_PRONOUNS.contains(mainForm)) {
            pronoun.setType(PronounType.Negative);
            pronoun.setQuantity(QUANTS.get(parts.get(0)));
            pronoun.setGender(GENDERS.get(parts.get(1)));
            pronoun.setWordcase(CASES.get(parts.get(3)));
            return pronoun;
        }
        if (INDETERMINATE_PRONOUNS.contains(mainForm)) {
            pronoun.setType(PronounType.Indeterminate);
            pronoun.setQuantity(QUANTS.get(parts.get(0)));
            pronoun.setGender(GENDERS.get(parts.get(1)));
            pronoun.setWordcase(CASES.get(parts.get(3)));
            return pronoun;
        }
        return null;
    }
    
    private Word parseProAdjective(List<String> parts,String mainForm) {
        Proadjective proadj = new Proadjective();
        if (mainForm.equals("их")) return null;
        if (INTERROGATIVE_PRONOUNS.contains(mainForm)) proadj.setType(PronounType.Interrogative);
        if (POSSESIVE_PRONOUNS.contains(mainForm)) proadj.setType(PronounType.Possesive);
        if (INDETERMINATE_PRONOUNS.contains(mainForm)) proadj.setType(PronounType.Indeterminate);
        if (NEGATIVE_PRONOUNS.contains(mainForm)) proadj.setType(PronounType.Negative);
        if (ATTRIBUTIVE_PRONOUNS.contains(mainForm)) proadj.setType(PronounType.Attributive);
        if (INDICATORY_PRONOUNS.contains(mainForm)) proadj.setType(PronounType.Indicatory);
        proadj.setDegree(CompareDegree.Normal);
        proadj.setIsFull(true);
        proadj.setWordCase(CASES.get(parts.get(0)));
        proadj.setQuantity(QUANTS.get(parts.get(1)));
        if (proadj.getQuantity() == Quantity.Singular) proadj.setGender(GENDERS.get(parts.get(2)));
        return proadj;
    }
    
    private Word parseProAdverb(List<String> parts,String mainForm) {
        Proadverb proadv = new Proadverb();
        if (INTERROGATIVE_PRONOUNS.contains(mainForm)) proadv.setType(PronounType.Interrogative);
        if (INDETERMINATE_PRONOUNS.contains(mainForm)) proadv.setType(PronounType.Indeterminate);
        if (NEGATIVE_PRONOUNS.contains(mainForm)) proadv.setType(PronounType.Negative);
        if (ATTRIBUTIVE_PRONOUNS.contains(mainForm)) proadv.setType(PronounType.Attributive);
        if (INDICATORY_PRONOUNS.contains(mainForm)) proadv.setType(PronounType.Indicatory);
        return proadv;
    }
}
